package com.ubi.core.framework;

import com.ubi.core.base.BaseMessage;
import com.ubi.core.framework.request.RequestWrapper;
import com.ubi.core.framework.response.ResponseWrapper;
import com.ubi.core.json.JsonConverter;
import com.ubi.core.log.TraceLogger;
import com.ubi.core.utils.Constants;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Enumeration;
import java.util.Map;

public class GlobalFilter extends OncePerRequestFilter {

    private final Logger logger = LoggerFactory.getLogger(GlobalFilter.class);

    private final JsonConverter jsonConverter = new JsonConverter();

    public static final String JSON_CONTENT_TYPE = "application/json";

    public static final String SUCCESS_MSG = "Http request successfully executed!";

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {
        TraceLogger traceLogger = TraceLogger.get();
        traceLogger.initialize();
        try {
            logger.debug("=== begin request processing ===");

            // Check request legal

            RequestWrapper requestWrapper = new RequestWrapper(request);
            ResponseWrapper responseWrapper = new ResponseWrapper(response);
            rewriteRequest(request, requestWrapper); //重写请求
            logRequest(requestWrapper);
            filterChain.doFilter(requestWrapper, responseWrapper);
            if(request.getRequestURI().contains(Constants.PROTOCAL_URI)){
                ServletOutputStream out = response.getOutputStream();
                out.write(responseWrapper.getResponseData());
                response.setContentType(JSON_CONTENT_TYPE);
                out.flush();
                out.close();
            }else{
                rewriteResponse(request, response, responseWrapper); // 重写返回值
            }
        } finally {
            logResponse(response);
            logger.debug("=== finish request processing ===");
            traceLogger.clearUp(shouldFlushTraceLog(request));
        }
    }

    //    private void checkRequestLegal(HttpServletRequest request) {
    //
    //    }

    protected void rewriteRequest(HttpServletRequest request, RequestWrapper requestWrapper) {
        if (this.isJsonpRequest(request)) {
            this.processJsonpRequest(request, requestWrapper);
        }
    }

    protected void processJsonpRequest(HttpServletRequest request, RequestWrapper requestWrapper) {
        Map<String, String[]> param = request.getParameterMap();
        if (MapUtils.isEmpty(param)) {
            return;
        }

        if (ArrayUtils.isNotEmpty(param.get("_site"))) {
            requestWrapper.putHeader("site", param.get("_site")[0]);
        }

        if (ArrayUtils.isNotEmpty(param.get("_os"))) {
            requestWrapper.putHeader("os", param.get("_os")[0]);
        }
    }

    boolean shouldFlushTraceLog(HttpServletRequest request) {
        return TraceLogger.get().shouldAlwaysFlushTraceLog() || BooleanUtils.toBoolean(request.getHeader("_traceLog"));
    }

    private void logRequest(HttpServletRequest request) {
        // log the request info
        logger.debug("requestURL={}", request.getRequestURL());
        logger.debug("serverName={}", request.getServerName());
        logger.debug("serverPort={}", request.getServerPort());
        logger.debug("contextPath={}", request.getContextPath());
        logger.debug("pathInfo={}", request.getPathInfo());
        logger.debug("queryString={}", request.getQueryString());
        logger.debug("requestMethod={}", request.getMethod());
        logRequestHeaders(request);
        logRequestParameters(request);
        logRequestBody(request);
    }

    private void logRequestBody(HttpServletRequest request) {
        // when upload file,the request will be converted to
        // DefaultMultipartHttpServletRequest
        if (!(request instanceof RequestWrapper)) {
            return;
        }
        String requestBody = ((RequestWrapper) request).getRequestBody();
        if (StringUtils.isNotEmpty(requestBody)) {
            logger.debug("requestBody={}", requestBody);
        }
    }

    private void logRequestParameters(HttpServletRequest request) {
        Enumeration paramNames = request.getParameterNames();
        while (paramNames.hasMoreElements()) {
            String paramName = (String) paramNames.nextElement();
            logger.debug("[param] {}={}", paramName, request.getParameter(paramName));
        }
    }

    private void logRequestHeaders(HttpServletRequest request) {
        Enumeration headers = request.getHeaderNames();
        while (headers.hasMoreElements()) {
            String headerName = (String) headers.nextElement();
            logger.debug("[header] {}={}", headerName, request.getHeader(headerName));
        }
    }

    private void logResponse(HttpServletResponse response) {
        int status = response.getStatus();
        logger.debug("responseHTTPStatus={}", status);
        logResponseHeaders(response);
    }

    private void logResponseHeaders(HttpServletResponse response) {
        for (String name : response.getHeaderNames()) {
            logger.debug("[response-header] {}={}", name, response.getHeader(name));
        }
    }

    private void rewriteResponse(HttpServletRequest request, HttpServletResponse response,
        ResponseWrapper responseWrapper) throws IOException {

        byte[] data = responseWrapper.getResponseData();

        HttpStatus httpStatus = HttpStatus.valueOf(response.getStatus());

        String responseStr = new String(data, Charset.defaultCharset());

        //TODO(Kent) 需判断content-type为application/json才可以
        // HttpStatus Code == 200
        if (HttpStatus.OK.equals(httpStatus) && data.length == 0) {
            BaseMessage baseMessage = new BaseMessage(HttpStatus.OK.value(), SUCCESS_MSG);
            responseStr = jsonConverter.toJson(baseMessage);
        }

        ServletOutputStream out = response.getOutputStream();
        Map<String, String[]> param = request.getParameterMap();
        if (this.isJsonpRequest(request)) {
            String callback = param.get("_cb")[0];
            out.write(callback.concat("(").getBytes(Charset.defaultCharset()));
            out.write(responseStr.getBytes(Charset.defaultCharset()));
            out.write(");".getBytes(Charset.defaultCharset()));
            response.setStatus(HttpStatus.OK.value());
        } else if (HttpStatus.OK.equals(httpStatus) && data.length > 0) {
            BaseMessage baseMessage = new BaseMessage(HttpStatus.OK.value(), SUCCESS_MSG);
            String baseMsg = jsonConverter.toJson(baseMessage);
            String baseMsgReplace = baseMsg.replace("}", ",");
            out.write(baseMsgReplace.getBytes(Charset.defaultCharset()));
            out.write("\"data\":".getBytes());
            out.write(responseStr.getBytes(Charset.defaultCharset()));
            out.write("}".getBytes());
        } else {
            out.write(responseStr.getBytes(Charset.defaultCharset()));
        }

        response.setContentType(JSON_CONTENT_TYPE);
        out.flush();
        out.close();
    }

    protected boolean isJsonpRequest(HttpServletRequest request) {
        Map<String, String[]> param = request.getParameterMap();
        return MapUtils.isNotEmpty(param) && ArrayUtils.isNotEmpty(param.get("_cb")) && StringUtils
            .isNotBlank(param.get("_cb")[0]);
    }
}
